In this tutorial, we will explore interactive graphs in R using three packages: * The digraphs package * the plotly package * the leaflet package

if(!require(knitr)) install.packages("knitr")
library(knitr)
print("You will not see messages or warnings.")
if(!require(devtools)) install.packages("devtools")
if(!require(R.utils)) install.packages("R.utils")
if(!require(htmlwidgets)) install.packages("htmlwidgets")
if(!require(htmltools)) install.packages("htmltools")
if(!require(readr)) install.packages("readr")
if (!require(devtools)) install.packages("devtools")

The next code chunk represents our Azure Databricks account URL. However, we must replace with the region of our account. We also need to make sure we use “https”.

databricksURL <- "https://<region>.azuredatabricks.net/files/rwidgets/"

Markdown Setup

The next code chunk sets up the function and directory for printing the html markdown this tutorial. Here, we replace with the workspace ID of our Azure Databricks Account URL

db_html_print <- function(x, ..., view = interactive()) {
  fileName <- paste(tempfile(), ".html", sep="")
  htmlwidgets::saveWidget(x, file = fileName)
  
  randomFileName = paste0(floor(runif(1, 0, 10^12)), ".html")
  baseDir <- "/dbfs/FileStore/rwidgets/"
  dir.create(baseDir)
  internalFile = paste0(baseDir, randomFileName)
  externalFile = paste0(databricksURL, randomFileName, "?o=<workspace-id>")
  system(paste("cp", fileName, internalFile))
  displayHTML(externalFile)
}
R.utils::reassignInPackage("print.htmlwidget", pkgName = "htmlwidgets", value = db_html_print)

The dygraphs Package

About dygraphs

The dygraphs package is an R interface to the dygraphs JavaScript charting library. It provides rich facilities for charting time-series data in R, including:

  • Automatically plots xts time series objects (or any object convertible to xts).
  • Highly configurable axis and series display (including optional second Y-axis).
  • Rich interactive features including zoom/pan and series/point highlighting.
  • Display upper/lower bars (e.g. prediction intervals) around series.
  • Various graph overlays including shaded regions, event lines, and point annotations.
  • Use at the R console just like conventional R plots (via RStudio Viewer).
  • Seamless embedding within R Markdown documents and Shiny web applications.

We can install the dygraphs package from CRAN as follows:

if(!require(dygraphs)) install.packages("dygraphs")
library(dygraphs)

We can use dygraphs at the R console, within R Markdown documents, and within Shiny applications. We show a few examples of dygraphs below.

dygraphs Examples

Here’s a simple dygraph created from a multiple time series object, namely male and female deaths from lung cancer. If we mouse-over each point on either graph, we will the information captured for each entry, i.e., Month, Year, number of male deaths, and female deaths over time.

library(dygraphs)
lungDeaths <- (cbind(mdeaths, fdeaths))
dygraph(lungDeaths)

Note that this graph is fully interactive: as we mouse moves over the series individual values are displayed. We can also select regions of the graph to zoom into (double-click zooms out).

We can customize dygraphs by piping additional commands onto the original dygraph object. Here we pipe a dyRangeSelector onto our original graph:

dygraph(lungDeaths) %>% dyRangeSelector()

Note that this example uses the %>% (or “pipe”) operator from the magrittr package to compose the dygraph with the range selector.

print("You will not see the text output.")
if(!require(magrittr)) install.packages("magrittr")
library(magrittr)

We use a similar syntax to customize axes, series, and other options. For example:

dygraph(lungDeaths) %>%
  dySeries("mdeaths", label = "Male") %>%
  dySeries("fdeaths", label = "Female") %>%
  dyOptions(stackedGraph = TRUE) %>%
  dyRangeSelector(height = 20)

There are many options for customizing series and axis display are available. It’s even possible to combine multiple lower/value/upper style series into a single display with shaded bars. Here’s an example that illustrates shaded bars, specifying a plot title, suppressing the drawing of the grid for the x axis, and the use of a custom palette for series colors:

hw <- HoltWinters(ldeaths)
predicted <- predict(hw, n.ahead = 72, prediction.interval = TRUE)

dygraph(predicted, main = "Predicted Lung Deaths (UK)") %>%
  dyAxis("x", drawGrid = FALSE) %>%
  dySeries(c("lwr", "fit", "upr"), label = "Deaths") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1"))

The plotly Oackage

Installing and Loading plotly

The next code chunk indicates that the plotly package is required for the code that follows. The code first checks to ensure that plotly has been installed and if not, it executes the installation and loads the library. We also suppress the messages and warnings that would otherwise appear. To download plotly from CRAN, use https://cran.r-project.org/web/packages/plotly/index.html.

print("You will not see the text output.")
if(!require(plotly)) install.packages("plotly")
library(plotly)

plotly Examples

Plotly’s R graphing library makes interactive, publication-quality graphs. Specifically, plotly is an R package for creating interactive web-based graphs via the open source JavaScript graphing library plotly.js. Graphs created with the plotly R package are rendered locally through the htmlwidgets framework. By default, the plotly R package runs locally in your web browser or in the RStudio viewer. Simply printing the plot object will render the chart. The example below generates box plots comparing the percent of college educated people living in the indicated midwest states. Mousing over the chart components reveal the underlying plot and data information.

p <- plot_ly(midwest, x = ~percollege, color = ~state, type = "box")
p

the leaflet Package

leaflet is one of the most popular open-source JavaScript libraries for interactive maps, and is implemented in R. This R package makes it easy to integrate and control leaflet maps in R. Here are some of the features of leaflet: * Create maps right from the R console or RStudio * Embed maps in knitr/R Markdown documents and Shiny apps * Easily render spatial objects from the sp or sf packages, or data frames with latitude/longitude columns * Use map bounds and mouse events to drive Shiny logic * Display maps in non spherical mercator projections * Augment map features using chosen plugins from leaflet plugins repository

Installing and Loading leaflet

The next code chunk loads the leaflet package in the same manner we loaded plotly.

print("You will not see the text output.")
if(!require(leaflet)) install.packages("leaflet")
library(leaflet)

leaflet Examples

The example below shows information about crimes committed in Chennai India, by typye of crime, the location of the crime and other details. We keep the data on our github site and downloaded it to our local directory, shown as the path below. However, we can also load the data directly from our github URL.

path <- "C:\\Users\\1061744157C\\Documents\\R Programming\\Data\\chennai_crimes\\chennai_crimes.csv"
crimeDF <- read_csv(path)
## 
## -- Column specification --------------------------------------------------------
## cols(
##   IncidntNum = col_double(),
##   Category = col_character(),
##   Descript = col_character(),
##   DayOfWeek = col_character(),
##   Date = col_character(),
##   Time = col_time(format = ""),
##   PdDistrict = col_character(),
##   Resolution = col_character(),
##   Address = col_character(),
##   X = col_double(),
##   Y = col_double(),
##   Location = col_character(),
##   PdId = col_double()
## )

The preceding column specification indicate the name and type of data comprising the variables in the set. For instance, “X = col_double()”, is the floating point numeric data format.

Exploring the data

The next code chuck revels the headings and first six rows of crime data.

head(crimeDF)
## # A tibble: 6 x 13
##   IncidntNum Category  Descript      DayOfWeek Date  Time  PdDistrict Resolution
##        <dbl> <chr>     <chr>         <chr>     <chr> <tim> <chr>      <chr>     
## 1  150098210 ROBBERY   ROBBERY, BOD~ Sunday    2/1/~ 15:45 Zone4      NONE      
## 2  150098210 AGG_ASSA~ AGGRAVATED A~ Sunday    2/1/~ 15:45 Zone4      NONE      
## 3  150098260 LARCENY/~ PETTY THEFT ~ Saturday  1/31~ 17:00 Zone3      ARREST    
## 4  150098345 LARCENY/~ PETTY THEFT ~ Sunday    2/1/~ 14:00 Zone5      ARREST    
## 5  150098367 ROBBERY   ROBBERY, ARM~ Sunday    2/1/~ 16:20 Zone5      ARREST    
## 6  150098395 LARCENY/~ PETTY THEFT ~ Sunday    2/1/~ 14:30 Zone7      ARREST    
## # ... with 5 more variables: Address <chr>, X <dbl>, Y <dbl>, Location <chr>,
## #   PdId <dbl>

Using datatable with leaflet

Here we convert the dataframe crimeDF to a datatable (DT package) dataframe.

message("You will not see the message.")
if(!require(DT)) install.packages("DT")
library(DT)

To ensure we get our intended reuslt (a dataframe), we examine its structure using str().

df <- as.data.frame(crimeDF)
str(df)
## 'data.frame':    499365 obs. of  13 variables:
##  $ IncidntNum: num  1.5e+08 1.5e+08 1.5e+08 1.5e+08 1.5e+08 ...
##  $ Category  : chr  "ROBBERY" "AGG_ASSAULT" "LARCENY/THEFT" "LARCENY/THEFT" ...
##  $ Descript  : chr  "ROBBERY, BODILY FORCE" "AGGRAVATED ASSAULT WITH BODILY FORCE" "PETTY THEFT SHOPLIFTING" "PETTY THEFT SHOPLIFTING" ...
##  $ DayOfWeek : chr  "Sunday" "Sunday" "Saturday" "Sunday" ...
##  $ Date      : chr  "2/1/2015" "2/1/2015" "1/31/2015" "2/1/2015" ...
##  $ Time      : 'hms' num  15:45:00 15:45:00 17:00:00 14:00:00 ...
##   ..- attr(*, "units")= chr "secs"
##  $ PdDistrict: chr  "Zone4" "Zone4" "Zone3" "Zone5" ...
##  $ Resolution: chr  "NONE" "NONE" "ARREST" "ARREST" ...
##  $ Address   : chr  "None" "None" "None" "None" ...
##  $ X         : num  80.3 80.3 80.3 80.3 80.2 ...
##  $ Y         : num  13.1 13.1 13.1 13.1 13.1 ...
##  $ Location  : chr  "(80.27569397,13.09199072)" "(80.27569397,13.09199072)" "(80.27280468,13.09405785)" "(80.27674581,13.0768748)" ...
##  $ PdId      : num  1.5e+13 1.5e+13 1.5e+13 1.5e+13 1.5e+13 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   IncidntNum = col_double(),
##   ..   Category = col_character(),
##   ..   Descript = col_character(),
##   ..   DayOfWeek = col_character(),
##   ..   Date = col_character(),
##   ..   Time = col_time(format = ""),
##   ..   PdDistrict = col_character(),
##   ..   Resolution = col_character(),
##   ..   Address = col_character(),
##   ..   X = col_double(),
##   ..   Y = col_double(),
##   ..   Location = col_character(),
##   ..   PdId = col_double()
##   .. )

Setting up the Data for Popups

We will generate interactive popups on the crime map that show the information contained in the data. To ensure the data is uniformly represented, we create a function that converts a character string with arbitrary case to a character string whose first character is capitalized and remaining characters are set to lower case.

proper_case <- function(x) {
  return (gsub("\\b([A-Z])([A-Z]+)", "\\U\\1\\L\\2" , x, perl=TRUE))
}

Loading dplyr

dplyr is a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges.

print("You will not see the text output.")
if(!require(dplyr)) install.packages("dplyr")
library(dplyr)

This code chunk uses the dplyr function mutate. mutate() adds new variables and preserves existing ones. This is were we implement the proper_case function.

df <- df %>% mutate(Category = proper_case(Category),
                    Descript = proper_case(Descript),
                    PdDistrict = proper_case(Location),
                    Resolution = proper_case(Resolution),
                    Time = as.character(Time))

Changing Data Formats

Often the data we need to analyze is not in the format we require, or it may not be formatted in a manner that leaflet (or other packages) work with. In particular, the X and Y coordinates our our set must be numeric and are required to produce a leaflet chart.

df$X <- as.numeric(df$X)
df$Y <- as.numeric(df$Y)

To see that the X and Y coordinates are numeric, we examine it in table format using head().

data <- df[1:10000,] 
head(data)
##   IncidntNum      Category                             Descript DayOfWeek
## 1  150098210       Robbery                Robbery, Bodily Force    Sunday
## 2  150098210   Agg_ASSAULT Aggravated Assault With Bodily Force    Sunday
## 3  150098260 Larceny/Theft              Petty Theft Shoplifting  Saturday
## 4  150098345 Larceny/Theft              Petty Theft Shoplifting    Sunday
## 5  150098367       Robbery          Robbery, Armed With A Knife    Sunday
## 6  150098395 Larceny/Theft         Petty Theft From Locked Auto    Sunday
##        Date     Time                PdDistrict Resolution Address        X
## 1  2/1/2015 15:45:00 (80.27569397,13.09199072)       None    None 80.26669
## 2  2/1/2015 15:45:00 (80.27569397,13.09199072)       None    None 80.26669
## 3 1/31/2015 17:00:00 (80.27280468,13.09405785)     Arrest    None 80.26380
## 4  2/1/2015 14:00:00  (80.27674581,13.0768748)     Arrest    None 80.26775
## 5  2/1/2015 16:20:00 (80.25790198,13.07291073)     Arrest    None 80.24890
## 6  2/1/2015 14:30:00 (80.23146124,13.07499994)     Arrest    None 80.22246
##          Y                  Location        PdId
## 1 13.09199 (80.27569397,13.09199072) 1.50098e+13
## 2 13.09199 (80.27569397,13.09199072) 1.50098e+13
## 3 13.09406 (80.27280468,13.09405785) 1.50098e+13
## 4 13.07687  (80.27674581,13.0768748) 1.50098e+13
## 5 13.07291 (80.25790198,13.07291073) 1.50098e+13
## 6 13.07500 (80.23146124,13.07499994) 1.50098e+13

Defining Popups

In this code chunk, we “paste” data headings inro a variable names data$popup. The code shows the variables names, like Category, there format (as we saw above), and line breaks (<>br).

data$popup <- paste("<b>Incident #: </b>", data$IncidntNum, 
                    "<br>", "<b>Category: </b>", data$Category,
                    "<br>", "<b>Description: </b>", data$Descript,
                    "<br>", "<b>Day of week: </b>", data$DayOfWeek,
                    "<br>", "<b>Date: </b>", data$Date,
                    "<br>", "<b>Time: </b>", data$Time,
                    "<br>", "<b>PD district: </b>", data$PdDistrict,
                    "<br>", "<b>Resolution: </b>", data$Resolution,
                    "<br>", "<b>Address: </b>", data$Location,
                    "<br>", "<b>Longitude: </b>", data$X,
                    "<br>", "<b>Latitude: </b>", data$Y)

Executing a leaflet Plot

The next code chunk execute the leaflet map with its previously defined pupups. It also provides three different map types or viewpoints.

leaflet(data, width = "100%") %>% addTiles() %>%
  addTiles(group = "OSM (default)") %>%
  addProviderTiles(provider = "Esri.WorldStreetMap",group = "World StreetMap") %>%
  addProviderTiles(provider = "Esri.WorldImagery",group = "World Imagery") %>%
  addProviderTiles(provider = "NASAGIBS.ViirsEarthAtNight2012",group = "Nighttime Imagery") %>%
  addMarkers(lng = ~Y, lat = ~X, popup = data$popup, clusterOptions = markerClusterOptions()) %>%
  addLayersControl(
    baseGroups = c("OSM (default)","World StreetMap", "World Imagery"),
    options = layersControlOptions(collapsed = FALSE)
  )
#data$Y
#data$popup

Another leaflet Example

The following interactive leaflet map shows the weather using weather data from 2012. It can be updated with current weather.

m <- leaflet() %>% addTiles() %>% setView(-93.65, 42.0285, zoom = 4) %>%
  addWMSTiles(
    "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi",
   layers = "nexrad-n0r-900913",
    options = WMSTileOptions(format = "image/png", transparent = TRUE),
    attribution = "Weather data © 2012 IEM Nexrad"
  )
m